import { NetCDFReader } from 'netcdfjs';

// 获取数组最值
const getMaxMin = arr => {
  let min = Infinity;
  let max = -Infinity;
  let hasNumber = false; // 添加变量记录数组是否有数字

  for (let num of arr) {
    if (!isNaN(num)) {
      hasNumber = true; // 如果有数字则更新状态

      if (num < min) {
        min = num;
      }
      if (num > max) {
        max = num;
      }
    }
  }

  if (!hasNumber) {
    // 如果数组没有数字则返回 0
    return { min: 0, max: 0 };
  }

  return { min, max };
};

let loadNetCDF = function (file, { fields, valueRange, offset }) {
  return new Promise(function (resolve, reject) {
    // const { U, V, W, H, lon, lat, lev } = fields;
    const reader = new FileReader();
    // 用readAsText读取文件文件内容
    reader.readAsArrayBuffer(file);

    reader.onload = function () {
      let arrayToMap = function (array) {
        return array.reduce(function (map, object) {
          map[object.name] = object;
          return map;
        }, {});
      };
      let NetCDF = new NetCDFReader(reader.result);
      let data = {};

      let variables = NetCDF.header.variables.map(item => item.name);
      for (let key in fields) {
        let arr = [];
        if (fields[key] && variables.indexOf(fields[key]) === -1) {
          arr.push(fields[key]);
        }
        if (arr.length) {
          reject('NetCDF file no such attribute: ' + arr + '\n all variables are: ' + variables);
          return;
        }
      }

      let dimensions = arrayToMap(NetCDF.dimensions);
      data.dimensions = {
        lon: 1,
        lat: 1,
        lev: 1
      };
      ['lon', 'lat', 'lev'].map(key => {
        try {
          if (fields[key]) {
            data.dimensions[key] = dimensions[fields[key]].size;
            let array = NetCDF.getDataVariable(fields[key])
              .flat()
              .map(item => {
                if (offset[key]) return item + offset[key];
                return item;
              });
            const minMax = getMaxMin(array);
            data[key] = {};
            data[key].array = new Float32Array(array);
            data[key].min = minMax.min;
            data[key].max = minMax.max;
          }
        } catch (e) {
          reject(e);
          return;
        }
      });

      ['U', 'V', 'W', 'H'].map(key => {
        try {
          if (fields[key]) {
            let array = NetCDF.getDataVariable(fields[key])
              .flat()
              .map(item => {
                if (item < valueRange.min || item > valueRange.max) return 0;
                return item;
              });
            const minMax = getMaxMin(array);
            data[key] = {};
            data[key].array = new Float32Array(array);
            data[key].min = minMax.min;
            data[key].max = minMax.max;
          }
        } catch (e) {
          reject(e);
          return;
        }
      });

      if (!data.lev) {
        data.lev = {
          array: [0].flat(),
          min: 0,
          max: 0
        };
      }

      if (!fields['W']) {
        data.W = {
          array: new Float32Array(data.U.array.length),
          min: 0,
          max: 0
        };
      }

      if (!fields['H']) {
        data.H = {
          array: new Float32Array(data.U.array.length),
          min: 0,
          max: 0
        };
        if (fields['lev']) {
          const { lon, lat, lev } = data.dimensions;
          for (let i = 0; i < lev; i++) {
            for (let j = 0; j < lat; j++) {
              for (let k = 0; k < lon; k++) {
                let index = i * (lon * lat) + j * lon + k;
                data.H.array[index] = data.lev.array[i];
              }
            }
          }
          data.H.min = Math.min(...data.lev.array);
          data.H.max = Math.max(...data.lev.array);
        }
      }

      resolve(data);
    };
  });
};

let loadData = async function (input, type, { fields, valueRange, offset }) {
  if (type === 'json') {
    return input;
  }

  try {
    let data = await loadNetCDF(input, {
      fields,
      valueRange,
      offset
    });
    return data;
  } catch (e) {
    console.log('load data error!');
    throw e;
  }
};

// 先找一个随机的像素点,以此像素点经纬度范围生成随机位置
let getValidRange = function (data) {
  const dimensions = [data.dimensions.lon, data.dimensions.lat, data.dimensions.lev];
  const minimum = [data.lon.min, data.lat.min, data.lev.min];
  const maximum = [data.lon.max, data.lat.max, data.lev.max];

  const interval = [
    (maximum[0] - minimum[0]) / (dimensions[0] - 1),
    (maximum[1] - minimum[1]) / (dimensions[1] - 1),
    dimensions[2] > 1 ? (maximum[2] - minimum[2]) / (dimensions[2] - 1) : 1.0
  ];

  data.lon.interval = interval[0];
  data.lat.interval = interval[1];
  data.lev.interval = interval[2];

  let id = Math.floor(Math.random() * data.U.array.length);

  // let z = Math.floor(id / (dimensions[0] * dimensions[1]));
  let left = id % (dimensions[0] * dimensions[1]);
  let y = Math.floor(left / dimensions[0]);
  let x = left % dimensions[0];

  let lon = Cesium.Math.randomBetween(minimum[0] + x * interval[0], minimum[0] + (x + 1) * interval[0]);
  let lat = Cesium.Math.randomBetween(minimum[1] + (y - 1) * interval[1], minimum[1] + y * interval[1]);
  // let lev = Cesium.Math.randomBetween(minimum[2] + (z - 1) * interval[2], minimum[2] + z * interval[2])
  let H = data.H.array[id] || 0;
  let U = data.U.array[id] || 0;
  let V = data.V.array[id] || 0;
  let W = data.W.array[id] || 0;

  return { lon, lat, H, U, V, W };
};

/**
 * 产生随机粒子信息
 * @param {Number} maxParticles 粒子数
 * @param {Object} data nc数据
 * @param {Object} AABBox 包围盒,有{minLon, minLat, minH, maxLon,maxLat, maxH}
 * @param {Number} deltaH H的高度
 * @returns [Object] 粒子信息
 */
let randomizeParticles = function (maxParticles, data, AABBox, deltaH) {
  let array = [];
  if (!data) {
    return array;
  }
  let H = deltaH ?? 0.0;
  while (array.length < maxParticles) {
    let info = getValidRange(data);
    if (AABBox) {
      if (
        AABBox.minLon <= info.lon &&
        AABBox.maxLon >= info.lon &&
        AABBox.minLat <= info.lat &&
        AABBox.maxLat >= info.lat &&
        AABBox.minH <= info.H + H &&
        AABBox.maxH >= info.H + H
      ) {
        array.push(info);
      }
    } else {
      array.push(info);
    }
  }
  return array;
};

// 根据层,行,列,得到值
function getDataValue(data, x, y, z) {
  const dimensions = [data.dimensions.lon, data.dimensions.lat, data.dimensions.lev];
  let id = y + x * dimensions[1] + z * dimensions[1] * dimensions[0];

  const minimum = [data.lon.min, data.lat.min, data.lev.min];
  const maximum = [data.lon.max, data.lat.max, data.lev.max];
  const interval = [
    (maximum[0] - minimum[0]) / (dimensions[0] - 1),
    (maximum[1] - minimum[1]) / (dimensions[1] - 1),
    dimensions[2] > 1 ? (maximum[2] - minimum[2]) / (dimensions[2] - 1) : 1.0
  ];

  let lon = minimum[0] + x * interval[0];
  let lat = minimum[1] + y * interval[1];

  let H = data.H.array[id] || 0;
  let U = data.U.array[id] || 0;
  let V = data.V.array[id] || 0;
  let W = data.W.array[id] || 0;

  return { lon, lat, H, U, V, W };
}

function randomizeParticlesSection(_maxParticles, data, Section) {
  if (!data) {
    return [];
  }
  const dimensions = [data.dimensions.lon, data.dimensions.lat, data.dimensions.lev];
  let ret = [];
  if (Section.type == 'x') {
    let x = parseInt(Section.value * dimensions[0]);
    if (x == dimensions[0]) {
      x--;
    }
    for (let y = 0; y < dimensions[1]; y++) {
      for (let z = 0; z < dimensions[2]; z++) {
        const item = getDataValue(data, x, y, z);
        ret.push(item);
      }
    }
  }
  if (Section.type == 'y') {
    for (let x = 0; x < dimensions[0]; x++) {
      let y = parseInt(Section.value * dimensions[1]);
      if (y == dimensions[1]) {
        y--;
      }
      for (let z = 0; z < dimensions[2]; z++) {
        const item = getDataValue(data, x, y, z);
        ret.push(item);
      }
    }
  }

  if (Section.type == 'z') {
    for (let x = 0; x < dimensions[0]; x++) {
      for (let y = 0; y < dimensions[1]; y++) {
        let z = parseInt(Section.value * dimensions[2]);
        if (z == dimensions[2]) {
          z--;
        }
        const item = getDataValue(data, x, y, z);
        ret.push(item);
      }
    }
  }

  return ret;
}

export { loadData, randomizeParticles, randomizeParticlesSection };
